<?php
/**
 * 文件上传类。
 *
 */
class LeeNT_Uploader
{
	/**
	 * 文件存在目录位置。
	 *
	 * @var string
	 */
	private $dir_save_path = NULL;
	
	/**
	 * 允许的最大字节数。
	 *
	 * @var int
	 */
	private $max_size = 512000;
	
	/**
	 * 允许的文件类型扩展名列表。
	 *
	 * @var array
	 */
	private $file_types = NULL;
	
	/**
	 * 已成功上传的文件数量。
	 *
	 * @var int
	 */
	private $file_count = 0;
	
	/**
	 * 存放错误信息。
	 *
	 * @var array
	 */
	private $errors = array();
	
	/**
	 * 构造函数。
	 *
	 * @param string $save_dir
	 */
	function __construct($save_dir = NULL)
	{
		$this->dir_save_path = $save_dir;
	}
	
	/**
	 * 上传单个文件。
	 *
	 * @param string $form_name
	 * @param boolean $hash_subdir_enabled
	 * @param string $save_dir
	 * @param string $filename_prefix
	 * @return LeeNT_FileInfo
	 */
	function startup($form_name, $hash_subdir_enabled = false, $save_dir = NULL, $filename_prefix = '')
	{
		if (!is_null($save_dir)) {
			$this->dir_save_path = $save_dir;
		}
		if (empty($this->dir_save_path)) {
			$this->errors[] = '文件保存目录路径尚未设置!';
			return false;
		}
		
		if (!(file_exists($this->dir_save_path) && is_dir($this->dir_save_path))) {
			if (mkdir($this->dir_save_path, 0777, true) == false) {
				$this->errors[] = '尝试创建目录过程中发生错误!(Path: ' . $this->dir_save_path . ')';
				return false;
			}
		}
		
		if (!(file_exists($this->dir_save_path) && is_dir($this->dir_save_path))) {
			$this->errors[] = '文件保存路径设置不正确!';
			return false;
		}
		
		if (!is_writable($this->dir_save_path)) {
			$this->errors[] = '目标目录没有设置写入权限!';
			return false;
		}
		
		$this->dir_save_path = $this->_ConvertPath(rtrim($this->dir_save_path, '\\\/'));
		
		$f = $_FILES[$form_name];
		
		if (empty($f)) {
			$this->errors[] = '未选定要上传的文件!';
			return false;
		}
		
		if (0 == $f['error']) {
			$file = new LeeNT_FileInfo();
			$file->OriginalFileName = $f['name'];
			$file->MimeType = $f['type'];
			$file->FileSize = $f['size'];
			
			if ($file->FileSize > $this->max_size) {
				$this->errors[] = '文件大小超出了系统限制(最大值: ' . number_format($this->max_size) . ' bytes)!';
				return false;
			}
			
			$fi = pathinfo($file->OriginalFileName);
			
			if (!empty($this->file_types) && !in_array(strtolower($fi['extension']), $this->file_types)) {
				$this->errors[] = '当前文件类型不允许上传!';
				return false;
			}
			
			$sub_dir_name = NULL;
			if ($hash_subdir_enabled == true) {
				$sub_dir_name = strval(time() % 2000);
			}
			
			$file->FileName = $this->_MakeRndName($filename_prefix) . '.' . strtolower($fi['extension']);
			$file->RelativePath = (is_null($sub_dir_name) ? '' : $sub_dir_name . '/') . $file->FileName;
			$file->FullPath = $this->dir_save_path . DS . (is_null($sub_dir_name) ? '' : $sub_dir_name . DS) . $file->FileName;
			
			$dirname = dirname($file->FullPath);
			if (!(file_exists($dirname) && is_dir($dirname))) {
				if (mkdir($dirname, 0777, true) == false) {
					$this->errors[] = '尝试创建目录过程中发生错误!';
					return false;
				}
			}
			
			if (move_uploaded_file($f['tmp_name'], $file->FullPath)) {
				return $file;
			} else {
				$this->errors[] = '转存文件过程中发生未知错误!';
				return false;
			}
		} else {
			switch ($f['error'])
			{
				case 1:
					$this->errors[] = 'The uploaded file exceeds the upload_max_filesize directive in php.ini.';
					break;
				case 2:
					$this->errors[] = 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.';
					break;
				case 3:
					$this->errors[] = 'The uploaded file was only partially uploaded.';
					break;
				case 4:
					$this->errors[] = 'No file was uploaded.';
					break;
				case 6:
					$this->errors[] = 'Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.';
					break;
				case 7:
					$this->errors[] = 'Failed to write file to disk. Introduced in PHP 5.1.0.';
					break;
				case 8:
					$this->errors[] = 'File upload stopped by extension. Introduced in PHP 5.2.0.';
					break;
			}
			return false;
		}
	}
	
	/**
	 * 设置允许的最大字节数。
	 *
	 * @param int $size
	 */
	function setMaxSize($size)
	{
		$this->max_size = $size;
	}
	
	/**
	 * 设置允许的扩展名(不包含 . 字符)。
	 *
	 * @param array|string $extName
	 */
	function setAllowExt($extName)
	{
		if (is_array($extName)) {
			foreach ($extName as $v) {
				$this->file_types[] = strtolower(strval($v));
			}
		} elseif (is_string($extName)) {
			$this->file_types[] = strtolower($extName);
		} else {
			trigger_error('设置扩展名数据格式不正确!', E_USER_ERROR);
		}
	}
	
	/**
	 * 设置保存目录路径。
	 *
	 * @param string $dir
	 */
	function setDir($dir)
	{
		$this->dir_save_path = $dir;
	}
	
	/**
	 * 返回错误信息集合。
	 *
	 * @return array
	 */
	function getErrors()
	{
		return $this->errors;
	}
	
	/**
	 * 返回第一条错误信息。
	 *
	 * @return string
	 */
	function getErrMsg()
	{
		return $this->errors[0];
	}
	
	/**
	 * 返回随机文件名称。
	 *
	 * @param string $prefix
	 * @return string
	 */
	private function _MakeRndName($prefix = NULL)
	{
		$sstr = split(" ", microtime(), 5);
		$seed = $sstr[0] * 10000;
		srand($seed);
		$random = sprintf('%05d', rand(1000, 10000));
		
		return ((!empty($prefix) ? $prefix : '') . date('YmdHis', time()) . $random);
	}
	
	/**
	 * 转换路径分隔符。
	 *
	 * @param string $filePath
	 * @return string
	 */
	private function _ConvertPath($filePath)
	{
		return preg_replace('/[\\/]+/', DS, $filePath);
	}
}

/**
 * 文件信息实体类。
 *
 */
class LeeNT_FileInfo
{
	/**
	 * 原始文件名称。
	 *
	 * @var string
	 */
	public $OriginalFileName;
	
	/**
	 * 文件名称。
	 *
	 * @var string
	 */
	public $FileName;
	
	/**
	 * 文件大小。
	 *
	 * @var int
	 */
	public $FileSize;
	
	/**
	 * MIME 类型名称。
	 *
	 * @var string
	 */
	public $MimeType;
	
	/**
	 * 完整物理路径。
	 *
	 * @var string
	 */
	public $FullPath;
	
	/**
	 * 相对路径。
	 *
	 * @var string
	 */
	public $RelativePath;
}